#include "StringUtil.h"
#include <algorithm>

std::wstring UTF8ToUnicode(const std::string &strUTF8) {
  std::wstring strUnicode;
  int nLen = ::MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, NULL, 0);

  if (nLen > 1) {
    wchar_t * pBuffer = new wchar_t[nLen + 1];
    memset(pBuffer, 0, nLen + 1);

    ::MultiByteToWideChar(CP_UTF8, 0, strUTF8.c_str(), -1, pBuffer, nLen);

    strUnicode = pBuffer;
    delete[] pBuffer;
  }

  return strUnicode;
}

std::string UnicodeToAnsi(const std::wstring &strUnicode) {
  std::string strAnsi;
  int nLen = ::WideCharToMultiByte(
               CP_ACP, 0, strUnicode.c_str(), -1, NULL, 0, NULL, NULL);

  if (nLen > 1) {
    char * pBuffer = new char[nLen + 1];
    memset(pBuffer, 0, nLen + 1);

    ::WideCharToMultiByte(
      CP_ACP, 0, strUnicode.c_str(), -1, pBuffer, nLen, NULL, NULL);

    strAnsi = pBuffer;
    delete[] pBuffer;
  }

  return strAnsi;
}

std::string UTF8ToAnsi(const std::string &strUTF8) {
  std::wstring strUnicode = UTF8ToUnicode(strUTF8);
  return UnicodeToAnsi(strUnicode);
}

std::wstring AnsiToUnicode(const std::string &strAnsi) {
  std::wstring strUnicode;
  int nLen = ::MultiByteToWideChar(CP_ACP, 0, strAnsi.c_str(), -1, NULL, 0);

  if (nLen > 1) {
    wchar_t * pBuffer = new wchar_t[nLen + 1];
    memset(pBuffer, 0, nLen + 1);

    ::MultiByteToWideChar(CP_ACP, 0, strAnsi.c_str(), -1, pBuffer, nLen);

    strUnicode = pBuffer;
    delete[] pBuffer;
  }

  return strUnicode;
}

std::string UnicodeToUTF8(const std::wstring &strUnicode) {
  std::string strUTF8;
  int nLen = ::WideCharToMultiByte(
               CP_UTF8, 0, strUnicode.c_str(), -1, NULL, 0, NULL, NULL);

  if (nLen > 1) {
    char * pBuffer = new char[nLen + 1];
    memset(pBuffer, 0, nLen + 1);

    ::WideCharToMultiByte(
      CP_UTF8, 0, strUnicode.c_str(), -1, pBuffer, nLen, NULL, NULL);

    strUTF8 = pBuffer;
    delete[] pBuffer;
  }

  return strUTF8;
}

std::string AnsiToUTF8(const std::string &strAnsi) {
  std::wstring strUnicode = AnsiToUnicode(strAnsi);
  return UnicodeToUTF8(strUnicode);
}

tstring& Trimmed(tstring &str) {
  if (str.empty()) return str;

  str.erase(0, str.find_first_not_of(_T(" ")));
  str.erase(str.find_last_not_of(_T(" ")) + 1);

  return str;
}

tstring Format(TCHAR const* format, ...) {
  std::vector<TCHAR> m_buffer;

  TCHAR const * str = 0;
  int ret = 0;
  va_list args;

  do {
    va_start(args, format);

    int printed;
    std::size_t const fmt_len = std::char_traits<TCHAR>::length(format);
    std::size_t buf_size = m_buffer.size();
    std::size_t const output_estimate = fmt_len + fmt_len / 2 + 1;
    if (output_estimate > buf_size)
      m_buffer.resize(buf_size = output_estimate);

    printed = _vsntprintf_s(&m_buffer[0], buf_size, _TRUNCATE, format, args);
    if (printed == -1) {
      buf_size *= 2;
      m_buffer.resize(buf_size);
    } else if (printed >= static_cast<int>(buf_size - 1)) {
      buf_size = printed + 2;
      m_buffer.resize(buf_size);
      printed = -1;
    } else
      m_buffer[printed] = 0;

    str = &m_buffer[0];
    ret = printed;

    va_end(args);
  } while (ret == -1);

  return ToTString(str);
}

std::vector<tstring> Split(const tstring &str, const tstring &delim) {
  std::vector<tstring> result;

  tstring strDest = str;
  TCHAR *token = NULL;
  TCHAR *next_token = NULL;
  token = _tcstok_s(const_cast<TCHAR*>(strDest.c_str()), delim.c_str(), &next_token);
  while (token != NULL) {
    result.push_back(token);
    token = _tcstok_s(NULL, delim.c_str(), &next_token);
  }

  return result;
}

tstring ToLower(const tstring &str) {
  tstring temp = str;
  transform(temp.begin(), temp.end(), temp.begin(), _totlower);
  return temp;
}

tstring ToUpper(const tstring &str) {
  tstring temp = str;
  transform(temp.begin(), temp.end(), temp.begin(), _totupper);
  return temp;
}

tstring CreateUUID() {
  tstring strUUID;
  GUID guid;
  HRESULT hr = ::CoCreateGuid(&guid);
  if (SUCCEEDED(hr)) {
    strUUID = Format(_T("{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}"),
                     guid.Data1, guid.Data2, guid.Data3,
                     guid.Data4[0], guid.Data4[1],
                     guid.Data4[2], guid.Data4[3],
                     guid.Data4[4], guid.Data4[5],
                     guid.Data4[6], guid.Data4[7]);
  }

  return strUUID;
}
